package eval;


import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.google.inject.Guice;
import com.google.inject.Injector;
import eval.gui.ResultsFrame;
import eval.guice.InjectorModule;
import eval.results.*;
import eval.utilities.configuration.ExperimentConfiguration;
import eval.exceptions.ConfigurationException;
import eval.experiment.Experiment;


/**
 * This is the Main class,, The program entry point
 * 
 * @author Amit Peled   <amit.kapina@gmail.com>
 * @author Igal Kaufman <igal.kaufman@gmail.com>
 *
 */
public class Main {
	
	
	/**
	 * Entry point of the program
	 * @param args the location of the configuration file
	 */
	public static void main(String[] args) {

		ExperimentConfiguration configuration = ExperimentConfiguration.getSingleton();

		loadConfiguration(args[0]);
		int numOFEngines = configuration.numOfEngines;
		//for seed
		Date date = new Date();
		int random = (int)date.getTime();

		List<IResultsData> resultsData = new ArrayList<IResultsData>();
		String resFileName = "CN_" + configuration.confSerialNum + "_" + ExperimentSerializer.generateFileName();

		//For every engine run experiment 
		for(int engine = 0 ; engine < numOFEngines ; engine++) {
			loadConfiguration(args[0]);
			setConfigParameters(engine,random,configuration);
			Injector injector = Guice.createInjector(new InjectorModule());
			Experiment experiment = new Experiment(injector);
			experiment.runExperiment(resFileName);
			//print result to CSV file
			IResultsData toPrint = experiment.getResultsData();
			ResultsToCsv print = new ResultsToCsv(toPrint,ExperimentSerializer.generateFileNameToCsv(resFileName,toPrint));
			print.write();
			resultsData.add(toPrint);
		}
		//Display GUI
		ResultsFrame gui = new ResultsFrame(resultsData,resFileName);
		gui.display();
	}
	
	/**
	 * Load configuration file system
	 * 
	 * @param configurationFilepath the location of the configuration file
	 */
	public static void loadConfiguration(String configurationFilepath) {
		ExperimentConfiguration provisionerConfiguration = ExperimentConfiguration
				.getSingleton();
		try {
			provisionerConfiguration.loadFromFile(configurationFilepath);
		} catch (ConfigurationException e) {
			e.printStackTrace();
			throw new RuntimeException("Error loading " + configurationFilepath
					+ ": " + e.getMessage());
		}

	}

	/*
	 * this function set all the non constant configuration parameters 
	 * of the configuration file.
	 *  
	 * @param engine - the current engine (e.g. CPLEX / OPENSTACK / FIREFIGHTING).
	 * @param random - the random seed
	 * @param configuration - input configuration Object.
	 * 
	 */
	private static void setConfigParameters(int engine, int random, ExperimentConfiguration configuration) {
		setEngine(engine,configuration);
		setHeuristicCompareGoal(engine,configuration);
		setSeedNumber(random,configuration);
		setHostBaseEnergyConsumption(configuration);
		setHostPowerOnConsumption(configuration);
		setNumberOfRelocation(configuration);

	}


	
	/*
	 * Determine the number of relocation allowed at optimization tick
	 *  
	 * @param configuration  configuration Object.
	 */
	private static void setNumberOfRelocation(ExperimentConfiguration configuration) {
		double maxNumberOfRelocation = 0;
		double optimizationPercentage = configuration.numberOfRelocation;
		double ongoingOptimizationPeriodPercentage =  (double)configuration.ongoingOptimizationPeriod / 100;
		double totalCloudCapacity = configuration.getCloudCapacity();
		double averageVMCPUConsumption = configuration.getAverageVMCPUConsumption();
		double numberOfVmToFillCloud = (totalCloudCapacity / averageVMCPUConsumption);
		maxNumberOfRelocation = (numberOfVmToFillCloud * (optimizationPercentage / 100) * ongoingOptimizationPeriodPercentage );
		configuration.setMaxNumberOfRelocation((int)maxNumberOfRelocation);
	}

	/*
	 * Determine the energy cost when host are turn on.
	 * 
	 * @param configuration  configuration Object.
	 */
	private static void setHostPowerOnConsumption(
			ExperimentConfiguration configuration) {
		// Power on penalty is equivalent to base during 10 time units
		int smallHostPowerON = (configuration.smallHostCpuCapacity * 400 / 100) ;
		int mediumHostPowerOn = (configuration.mediumHostCpuCapacity * 300 / 100);
		int LargeHostPowerOn = (configuration.largeHostCpuCapacity * 200 / 100);

		configuration.setHostPowerOnConsumption(smallHostPowerON, mediumHostPowerOn, LargeHostPowerOn);
	}

	/*
	 * Determine the energy cost of empty host
	 * 
	 * @param configuration  configuration Object.
	 */
	private static void setHostBaseEnergyConsumption(
			ExperimentConfiguration configuration) {
		int smallHostBaseEnergy = (int)(configuration.smallHostCpuCapacity * 40 / 100) ;
		int mediumHostBaseEnergy = (int)(configuration.mediumHostCpuCapacity * 30 / 100);
		int LargeHostBaseEnergy = (int)(configuration.largeHostCpuCapacity * 20 / 100);

		configuration.setHostBaseEnergyConsumption(smallHostBaseEnergy, mediumHostBaseEnergy, LargeHostBaseEnergy);

	}

	

	/*
	 * Determine the current running engine
	 * @param engineIndex 
	 * @param configuration  configuration Object.
	 */
	private static void setEngine(int engineIndex , ExperimentConfiguration configuration){
		switch(engineIndex){
		case 0:
			configuration.setOptimizationGoal(configuration.FirstEngine) ;
			break;
		case 1:
			configuration.setOptimizationGoal(configuration.SecondEngine);
			break;
		case 2:
			configuration.setOptimizationGoal(configuration.ThirdEngine);
			break;
		default:
			throw new IllegalArgumentException("The wrong Engine was chosen.");
		}


	}

	/*
	 * Determine how to compare between hosts
	 * @param HeuristicIndex 
	 * @param configuration configuration Object.
	 */
	private static void setHeuristicCompareGoal(int HeuristicIndex,ExperimentConfiguration configuration) {
		switch(HeuristicIndex){
		case 0:
			configuration.setHeuristicCompareGoal(configuration.FirstHeuristic) ;
			break;
		case 1:
			configuration.setHeuristicCompareGoal(configuration.SeconHeuristic);
			break;
		case 2:
			configuration.setHeuristicCompareGoal(configuration.ThirdHeuristick);
			break;
		default:
			throw new IllegalArgumentException("The wrong Heuristic was chosen.");

		}

	}
	
	/*
	 * Determine the seed 
	 * 
	 * @param random the seed
	 * @param configuration 
	 */
	private static void setSeedNumber(int random,ExperimentConfiguration configuration){
		if(configuration.randomSeed == 0){
			configuration.setRandomSeed(random);
		}
	}

}
